﻿/* EntitiesToDTOs. Copyright (c) 2011. Fabian Fernandez.
 * http://entitiestodtos.codeplex.com
 * Licensed by Common Development and Distribution License (CDDL).
 * http://entitiestodtos.codeplex.com/license
 * Fabian Fernandez. 
 * http://www.linkedin.com/in/fabianfernandezb/en
 * */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EntitiesToDTOs.Properties;
using System.Xml.Linq;
using EntitiesToDTOs.Domain;

namespace EntitiesToDTOs.Helpers
{
    /// <summary>
    /// Helps in the tasks relative to Properties of the EDMX
    /// </summary>
    internal class PropertyHelper
    {
        /// <summary>
        /// List of reserved property names
        /// </summary>
        private static List<string> ReservedNames
        {
            get
            {
                if (_reservedNames == null)
                {
                    _reservedNames = new List<string>();
                    _reservedNames.Add(Resources.ReservedName01);
                    _reservedNames.Add(Resources.ReservedName02);
                    _reservedNames.Add(Resources.ReservedName03);
                    _reservedNames.Add(Resources.ReservedName04);
                    _reservedNames.Add(Resources.ReservedName05);
                    _reservedNames.Add(Resources.ReservedName06);
                    _reservedNames.Add(Resources.ReservedName07);
                    _reservedNames.Add(Resources.ReservedName08);
                    _reservedNames.Add(Resources.ReservedName09);
                    _reservedNames.Add(Resources.ReservedName10);
                    _reservedNames.Add(Resources.ReservedName11);
                    _reservedNames.Add(Resources.ReservedName12);
                    _reservedNames.Add(Resources.ReservedName13);
                    _reservedNames.Add(Resources.ReservedName14);
                    _reservedNames.Add(Resources.ReservedName15);
                    _reservedNames.Add(Resources.ReservedName16);
                    _reservedNames.Add(Resources.ReservedName17);
                    _reservedNames.Add(Resources.ReservedName18);
                    _reservedNames.Add(Resources.ReservedName19);
                    _reservedNames.Add(Resources.ReservedName20);
                    _reservedNames.Add(Resources.ReservedName21);
                    _reservedNames.Add(Resources.ReservedName22);
                    _reservedNames.Add(Resources.ReservedName23);
                    _reservedNames.Add(Resources.ReservedName24);
                    _reservedNames.Add(Resources.ReservedName25);
                    _reservedNames.Add(Resources.ReservedName26);
                    _reservedNames.Add(Resources.ReservedName27);
                    _reservedNames.Add(Resources.ReservedName28);
                    _reservedNames.Add(Resources.ReservedName29);
                    _reservedNames.Add(Resources.ReservedName30);
                    _reservedNames.Add(Resources.ReservedName31);
                    _reservedNames.Add(Resources.ReservedName32);
                    _reservedNames.Add(Resources.ReservedName33);
                    _reservedNames.Add(Resources.ReservedName34);
                    _reservedNames.Add(Resources.ReservedName35);
                    _reservedNames.Add(Resources.ReservedName36);
                    _reservedNames.Add(Resources.ReservedName37);
                    _reservedNames.Add(Resources.ReservedName38);
                    _reservedNames.Add(Resources.ReservedName39);
                    _reservedNames.Add(Resources.ReservedName40);
                    _reservedNames.Add(Resources.ReservedName41);
                    _reservedNames.Add(Resources.ReservedName42);
                    _reservedNames.Add(Resources.ReservedName43);
                    _reservedNames.Add(Resources.ReservedName44);
                    _reservedNames.Add(Resources.ReservedName45);
                    _reservedNames.Add(Resources.ReservedName46);
                    _reservedNames.Add(Resources.ReservedName47);
                    _reservedNames.Add(Resources.ReservedName48);
                    _reservedNames.Add(Resources.ReservedName49);
                    _reservedNames.Add(Resources.ReservedName50);
                    _reservedNames.Add(Resources.ReservedName51);
                    _reservedNames.Add(Resources.ReservedName52);
                    _reservedNames.Add(Resources.ReservedName53);
                    _reservedNames.Add(Resources.ReservedName54);
                    _reservedNames.Add(Resources.ReservedName55);
                    _reservedNames.Add(Resources.ReservedName56);
                    _reservedNames.Add(Resources.ReservedName57);
                    _reservedNames.Add(Resources.ReservedName58);
                    _reservedNames.Add(Resources.ReservedName59);
                    _reservedNames.Add(Resources.ReservedName60);
                    _reservedNames.Add(Resources.ReservedName61);
                    _reservedNames.Add(Resources.ReservedName62);
                    _reservedNames.Add(Resources.ReservedName63);
                    _reservedNames.Add(Resources.ReservedName64);
                    _reservedNames.Add(Resources.ReservedName65);
                    _reservedNames.Add(Resources.ReservedName66);
                    _reservedNames.Add(Resources.ReservedName67);
                    _reservedNames.Add(Resources.ReservedName68);
                    _reservedNames.Add(Resources.ReservedName69);
                    _reservedNames.Add(Resources.ReservedName70);
                    _reservedNames.Add(Resources.ReservedName71);
                    _reservedNames.Add(Resources.ReservedName72);
                    _reservedNames.Add(Resources.ReservedName73);
                    _reservedNames.Add(Resources.ReservedName74);
                    _reservedNames.Add(Resources.ReservedName75);
                    _reservedNames.Add(Resources.ReservedName76);
                    _reservedNames.Add(Resources.ReservedName77);
                }

                return _reservedNames;
            }
        }
        private static List<string> _reservedNames = null;

        /// <summary>
        /// ComplexType DTOs available
        /// </summary>
        private static List<EntityDTO> ComplexTypes { get; set; }


        /// <summary>
        /// Set the Complex Types available
        /// </summary>
        /// <param name="complexTypes"></param>
        public static void SetComplexTypes(List<EntityDTO> complexTypes)
        {
            PropertyHelper.ComplexTypes = complexTypes;
        }

        /// <summary>
        /// Gets the CSharp Type from a EDMX Property
        /// </summary>
        /// <param name="propertyNode">Type attribute</param>
        /// <param name="entityOwnerName">Entity owner name</param>
        /// <returns></returns>
        public static string GetTypeFromEDMXProperty(XElement propertyNode, string entityOwnerName)
        {   
            // Get the Type attribute
            XAttribute typeAttribute = propertyNode.Attribute(EdmxNodeAttributes.Property_Type);

            // Check Type attribute exists
            if (typeAttribute == null)
            {
                string propertyName = propertyNode.Attribute(EdmxNodeAttributes.Property_Name).Value;

                throw new Exception(string.Format(Resources.Error_PropertyTypeAttributeMissing, 
                    entityOwnerName, propertyName));
            }

            // Get the Type value
            string typeValue = propertyNode.Attribute(EdmxNodeAttributes.Property_Type).Value;

            // Check Type value is not empty
            if (string.IsNullOrWhiteSpace(typeValue))
            {
                string propertyName = propertyNode.Attribute(EdmxNodeAttributes.Property_Name).Value;

                throw new Exception(string.Format(Resources.Error_PropertyTypeAttributeMissing,
                    entityOwnerName, propertyName));
            }

            // Check if it is Nullable
            bool isNullable = true;
            XAttribute nullableAttribute = propertyNode.Attribute(EdmxNodeAttributes.Property_Nullable);
            if (nullableAttribute != null)
            {
                isNullable = (nullableAttribute.Value == Resources.XmlBoolTrue);
            }

            // Variables
            string outputType = null;
            bool outputTypeAdmitsNullable = false;

            #region Type mapping
            if (typeValue == Resources.EdmxTypeBinary)
            {
                outputType = Resources.CSharpTypeBinary;
                outputTypeAdmitsNullable = false;
            }
            else if (typeValue == Resources.EdmxTypeBoolean)
            {
                outputType = Resources.CSharpTypeBoolean;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeByte)
            {
                outputType = Resources.CSharpTypeByte;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeDateTime)
            {
                outputType = Resources.CSharpTypeDateTime;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeDateTimeOffset)
            {
                outputType = Resources.CSharpTypeDateTimeOffset;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeDecimal)
            {
                outputType = Resources.CSharpTypeDecimal;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeDouble)
            {
                outputType = Resources.CSharpTypeDouble;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeGuid)
            {
                outputType = Resources.CSharpTypeGuid;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeInt16)
            {
                outputType = Resources.CSharpTypeInt16;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeInt32)
            {
                outputType = Resources.CSharpTypeInt32;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeInt64)
            {
                outputType = Resources.CSharpTypeInt64;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeSByte)
            {
                outputType = Resources.CSharpTypeSByte;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeSingle)
            {
                outputType = Resources.CSharpTypeSingle;
                outputTypeAdmitsNullable = true;
            }
            else if (typeValue == Resources.EdmxTypeString)
            {
                outputType = Resources.CSharpTypeString;
                outputTypeAdmitsNullable = false;
            }
            else if (typeValue == Resources.EdmxTypeTime)
            {
                outputType = Resources.CSharpTypeTime;
                outputTypeAdmitsNullable = true;
            }
            else
            {
                // Split typeValue to check if it is a Complex Type
                string[] typeValueSplitted = 
                    typeValue.Split(new string[] { Resources.Dot }, StringSplitOptions.RemoveEmptyEntries);

                // Get the Complex Type EDMX name (if it is one)
                string complexTypeEDMXName = typeValueSplitted[(typeValueSplitted.Length - 1)];

                // Search for the Complex Type in the list of Complex Types availables
                EntityDTO complexTypeDTO = PropertyHelper.ComplexTypes.FirstOrDefault(ct => ct.EntityName == complexTypeEDMXName);

                if (complexTypeDTO != null)
                {
                    // It is a Complex Type
                    outputType = complexTypeDTO.DTOName;
                    outputTypeAdmitsNullable = true;
                }
                else
                {
                    // Not a supported Type neither a Complex Type, use object instead and notify the user

                    outputType = Resources.CSharpTypeObject;
                    outputTypeAdmitsNullable = true;

                    string propertyName = propertyNode.Attribute(EdmxNodeAttributes.Property_Name).Value;

                    WarningsPool.AddWarning(string.Format(Resources.Warning_NotSupportedEDMXPropertyType,
                        entityOwnerName, propertyName, typeValue));
                }
            }
            #endregion Type mapping

            // Check if it is Nullable and the Type admits Nullable Types
            if (isNullable && outputTypeAdmitsNullable)
            {
                outputType = string.Format(Resources.CSharpTypeNullableT, outputType);
            }

            return outputType;
        }

        /// <summary>
        /// Gets a valid Property name
        /// </summary>
        /// <param name="propertyNameDesired">Property name desired</param>
        /// <param name="entityOwnerName">Entity owner name</param>
        /// <returns></returns>
        public static string GetPropertyName(string propertyNameDesired, string entityOwnerName)
        {
            if (string.IsNullOrWhiteSpace(propertyNameDesired))
            {
                throw new Exception(string.Format(Resources.Error_PropertyNameCannotBeEmpty, entityOwnerName));
            }

            if (PropertyHelper.ReservedNames.Contains(propertyNameDesired.ToLower()))
            {
                string newPropertyName = (propertyNameDesired + Resources.NameSeparator);

                WarningsPool.AddWarning(string.Format(Resources.Warning_InvalidPropertyName,
                    entityOwnerName, propertyNameDesired, newPropertyName));

                return newPropertyName;
            }
            else
            {
                return propertyNameDesired;
            }
        }
    }
}